#include "rendering/Scene.h"
#include "rendering/RootNode.h"
#include "rendering/CameraNode.h"
#include "Timer.h"
#include "event/Events.h"
#include "event/IMessengerEventData.h"
#include "ConfigurationManager.h"
#include "component/Renderer.h"



using namespace Boon;
using namespace Rendering;


Scene::Scene(void)
	:	m_Root( new RootNode() ),
		m_Camera( new CameraNode() ),
		m_Messenger( Messenger::GetInstance() ),
		m_EventFrameRendered( new Event_FrameRendered() )
{
	
}

void Boon::Rendering::Scene::Init()
{
	EventListener listener( this, &Scene::OnNodePositionUpdate );
	m_Messenger.Register( Event_GameObject_Moved::ID, listener );
	listener.bind( this, &Scene::OnAddNewSceneNode );
	m_Messenger.Register( Event_Add_SceneNode::ID, listener );
	listener.bind( this, &Scene::OnCreatedRenderComponent );
	m_Messenger.Register( Event_RenderComp_Created::ID, listener );
}

void Boon::Rendering::Scene::Unload()
{
	EventListener listener( this, &Scene::OnNodePositionUpdate );
	m_Messenger.Remove( Event_GameObject_Moved::ID, listener );
	listener.bind( this, &Scene::OnAddNewSceneNode );
	m_Messenger.Remove( Event_Add_SceneNode::ID, listener );
	listener.bind( this, &Scene::OnCreatedRenderComponent );
	m_Messenger.Remove( Event_RenderComp_Created::ID, listener );
}

Scene::~Scene(void)
{
	
}

bool Boon::Rendering::Scene::OnRender()
{
	m_Messenger.Bang( m_EventFrameRendered );

	if( m_Root && m_Camera )
	{
		//todo: set camera viewtrans
		if( m_Root->PreRender( this ) )
		{
			m_Root->Render( this );
			m_Root->RenderChildren( this );
			m_Root->PostRender( this );
			return true;
		}
	}
	else
	{
		throw BoonException( "Scene",
							 "OnRender()",
							 "Root/Camera were not initialized correctly; No rendering possible! " );
	}
	return false;
}

bool Boon::Rendering::Scene::OnRestore()
{
	return true;
}

bool Boon::Rendering::Scene::OnLostDevice()
{
	return true;
}

bool Boon::Rendering::Scene::Update()
{
	return m_Root->Update();
}

bool Boon::Rendering::Scene::AddNode( GameObjectID id, const ISceneNodeStrong& child )
{
	auto result = m_SceneNodes.find( id );
	if( result == m_SceneNodes.end() )
	{
		if( child->Get()->GetRenderPass() != PASS_NOT_RENDERED )
			m_SceneNodes[id] = child;
		m_Root->AddChild( child );
		return true;
	}
	return false;
}

bool Boon::Rendering::Scene::RemoveNode( GameObjectID id )
{
	auto result = m_SceneNodes.find( id );
	if( result != m_SceneNodes.end() )
	{
		m_Root->RemoveChild( result->second );
		m_SceneNodes.erase( result );
		return true;
	}
	return false;
}

void Boon::Rendering::Scene::ParentNode( const ISceneNodeStrong& child, const ISceneNodeStrong& parent )
{
	ISceneNodeWeak curParent( child->GetParent() );
	if( curParent )
		curParent.Lock()->RemoveChild( child->Get()->GameObjectID() );
	parent->AddChild( child );
}

void Boon::Rendering::Scene::SetCamera( const Shared_ptr<CameraNode>& camera )
{

}

ISceneNodeWeak Boon::Rendering::Scene::GetNode( GameObjectID id )
{
	auto result = m_SceneNodes.find( id );
	if( result != m_SceneNodes.end() )
		return result->second;
	return ISceneNodeWeak();
}

void Boon::Rendering::Scene::OnNodePositionUpdate( const IMessengerEventStrong& edata )
{
	Event_GameObject_Moved* event = static_cast<Event_GameObject_Moved*>( edata.Get() );
	ISceneNodeWeak sn = GetNode( event->GetGameObjectID() );
	if( !sn.Empty() )
	{
		sn.Lock()->SetTransform( event->GetMatrixToWorld(), event->GetMatrixFromWorld() );
	}
}

void Boon::Rendering::Scene::OnAddNewSceneNode( const IMessengerEventStrong& edata )
{
	Event_Add_SceneNode* event = static_cast<Event_Add_SceneNode*>( edata.Get() );
	m_Root->AddChild( event->m_NewNode );
}


void Boon::Rendering::Scene::OnCreatedRenderComponent( const IMessengerEventStrong& edata )
{
	//fetch game object with id received by event and init scene node
	Event_RenderComp_Created* event = static_cast<Event_RenderComp_Created*>( edata.Get() );
	event->GetNewNode()->Init( event->GetRenderer() );
	// finally add to scene
	AddNode( event->GetRenderer().GetGameObject().GetUniqueObjectID(), event->GetNewNode() );
}



